动态IL织入框架Harmony简单入手

您所在的位置:网站首页 harmony class 动态IL织入框架Harmony简单入手

动态IL织入框架Harmony简单入手

2024-01-28 19:26| 来源: 网络整理| 查看: 265

Harmony是一个开放源代码库,旨在在运行时替换、修饰或修改任何现有C#方法。它的主要用在用Mono语言编写的游戏和插件,但是该技术可以与任何.NET版本一起使用。它还照顾对同一方法的多次更改(它们累积而不是覆盖)。

它为每个原始方法创建DynamicMethod方法,并向其织入代码,该代码在开始和结束时调用自定义方法。它还允许您编写过滤器来处理原始的IL代码,从而可以对原始方法进行更详细的操作。

文档可以在这里找到。

最新2.0版本终于支持.net core.

Harmony支持手动(Patch)和自动(PatchAll)织入

织入位置可以是执行前(Prefix)、执行后(Postfix)和终结嚣(Finalizer),也可以更详细的手动修改IL(Transpiler)

支持构造函数、Getter/Setter、虚/非虚方法、静态方法

手动模式

class NoneGenericClass { private readonly bool _isRunning = true; private int _counter = 1; public int DoSomething() { Console.WriteLine(nameof(DoSomething)); if (_isRunning) { _counter++; } return _counter * 10; } public static int DoSomething2() { Console.WriteLine(nameof(DoSomething2)); return 3333; } public IEnumerable GetNumbers() { Console.WriteLine(nameof(GetNumbers)); yield return 1; yield return 2; yield return 3; } } static class NoneGenericClassPatcher { public static void Patch() { var harmony = new Harmony(nameof(NoneGenericClassPatcher)); harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.DoSomething)), new HarmonyMethod(GetMethod(nameof(MyPrefix))), new HarmonyMethod(GetMethod(nameof(MyPostfix))), new HarmonyMethod(GetMethod(nameof(MyTranspiler))), new HarmonyMethod(GetMethod(nameof(MyFinalizer)))); Console.WriteLine(new NoneGenericClass().DoSomething()); Console.WriteLine(); harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.GetNumbers)), new HarmonyMethod(GetMethod(nameof(MyPrefix))), new HarmonyMethod(GetMethod(nameof(PassthroughPostfix))), new HarmonyMethod(GetMethod(nameof(MyTranspiler))), new HarmonyMethod(GetMethod(nameof(MyFinalizer)))); Console.WriteLine(string.Join(", ", new NoneGenericClass().GetNumbers())); //BUG:有Finalizer方法时PassthroughPostfix不生效 Console.WriteLine(); harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.DoSomething2)), new HarmonyMethod(GetMethod(nameof(StaticPrefix))), new HarmonyMethod(GetMethod(nameof(MyPostfix))), new HarmonyMethod(GetMethod(nameof(MyTranspiler))), new HarmonyMethod(GetMethod(nameof(MyFinalizer)))); Console.WriteLine(NoneGenericClass.DoSomething2()); } static MethodInfo GetMethod(string name) => typeof(NoneGenericClassPatcher).GetMethod(name, BindingFlags.Static | BindingFlags.Public); public static bool MyPrefix(out Stopwatch __state, ref bool ____isRunning) { __state = Stopwatch.StartNew(); Console.WriteLine($"{nameof(MyPrefix)} {____isRunning}"); return true; } public static bool StaticPrefix(out Stopwatch __state) { __state = Stopwatch.StartNew(); Console.WriteLine($"{nameof(StaticPrefix)}"); return true; } public static void MyPostfix(Stopwatch __state, ref int __result, MethodBase __originalMethod) { Console.WriteLine($"{__state.ElapsedMilliseconds} {__result++}"); Console.WriteLine(nameof(MyPostfix)); } public static IEnumerable PassthroughPostfix(IEnumerable values) { yield return 0; foreach (var value in values) if (value > 1) yield return value * 10; yield return 99; Console.WriteLine(nameof(PassthroughPostfix)); } // looks for STDFLD someField and inserts CALL MyExtraMethod before it public static IEnumerable MyTranspiler(IEnumerable instructions) { Console.WriteLine(nameof(MyTranspiler)); //var found = false; foreach (var instruction in instructions) { //if (instruction.opcode == OpCodes.Stfld && instruction.operand == f_someField) //{ // yield return new CodeInstruction(OpCodes.Call, m_MyExtraMethod); // found = true; //} yield return instruction; } //if (found == false) // ReportError("Cannot find in OriginalType.OriginalMethod"); } public static void MyFinalizer(Exception __exception) { Console.WriteLine($"{nameof(MyFinalizer)} {__exception}"); } }

自动模式

public class Annotations { private readonly bool _isRunning; public IEnumerable GetNumbers() { Console.WriteLine(nameof(GetNumbers)); yield return 1; yield return 2; yield return 3; } } [HarmonyPatch(typeof(Annotations))] [HarmonyPatch(nameof(Annotations.GetNumbers))] public class AnnotationsPatcher { static AccessTools.FieldRef isRunningRef = AccessTools.FieldRefAccess("_isRunning"); public static void Patch() { var harmony = new Harmony(nameof(AnnotationsPatcher)); harmony.PatchAll(); Console.WriteLine(string.Join(", ", new Annotations().GetNumbers())); } static bool Prefix(Annotations __instance) { Console.WriteLine("Prefix"); return true; } /// Not working static IEnumerable Postfix(IEnumerable values) { yield return 0; foreach (var value in values) if (value > 1) yield return value * 10; yield return 99; Console.WriteLine(nameof(Postfix)); } // looks for STDFLD someField and inserts CALL MyExtraMethod before it public static IEnumerable Transpiler(IEnumerable instructions) { Console.WriteLine(nameof(Transpiler)); //var found = false; foreach (var instruction in instructions) { //if (instruction.opcode == OpCodes.Stfld && instruction.operand == f_someField) //{ // yield return new CodeInstruction(OpCodes.Call, m_MyExtraMethod); // found = true; //} yield return instruction; } //if (found == false) // ReportError("Cannot find in OriginalType.OriginalMethod"); } }

运行代码

static void Main(string[] args) { NoneGenericClassPatcher.Patch(); Console.WriteLine(); AnnotationsPatcher.Patch(); }

输出结果

MyTranspiler MyPrefix True DoSomething 20 MyPostfix MyFinalizer MyTranspiler MyPrefix True MyFinalizer GetNumbers 1, 2, 3 MyTranspiler StaticPrefix DoSomething2 3333 MyPostfix MyFinalizer Transpiler Prefix GetNumbers Postfix 0, 20, 30, 99


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3